package com.hsjk.szwj.module.train.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.net.URLEncodeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hsjk.szwj.framework.common.constant.CommonConstant;
import com.hsjk.szwj.framework.common.constant.RedisKeyConstant;
import com.hsjk.szwj.framework.common.exception.ServiceException;
import com.hsjk.szwj.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.hsjk.szwj.framework.common.pojo.PageResult;
import com.hsjk.szwj.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.hsjk.szwj.framework.security.core.util.SecurityFrameworkUtils;
import com.hsjk.szwj.framework.tenant.core.aop.TenantIgnore;
import com.hsjk.szwj.framework.tenant.core.context.TenantContextHolder;
import com.hsjk.szwj.module.member.api.user.MemberUserApi;
import com.hsjk.szwj.module.member.api.user.dto.MemberUserRespDTO;
import com.hsjk.szwj.module.pay.api.IMchDivisionReceiverApi;
import com.hsjk.szwj.module.pay.api.dto.MchDivisionReceiverDTO;
import com.hsjk.szwj.module.system.api.tenant.TenantApi;
import com.hsjk.szwj.module.system.api.tenant.dto.TenantRespDTO;
import com.hsjk.szwj.module.train.controller.admin.vo.AdminCourseDetailRespVO;
import com.hsjk.szwj.module.train.controller.admin.vo.AdminCoursePageReqVO;
import com.hsjk.szwj.module.train.controller.admin.vo.AdminCoursePageRespVO;
import com.hsjk.szwj.module.train.controller.admin.vo.AdminCreateCourseReqVO;
import com.hsjk.szwj.module.train.controller.admin.vo.AdminMemberCoursePageReqVO;
import com.hsjk.szwj.module.train.controller.admin.vo.AdminMemberCoursePageRespVO;
import com.hsjk.szwj.module.train.controller.admin.vo.AdminPlatformCoursePageReqVO;
import com.hsjk.szwj.module.train.controller.admin.vo.AdminPlatformCoursePageRespVO;
import com.hsjk.szwj.module.train.controller.admin.vo.AdminSetSkuDivisionReqVO;
import com.hsjk.szwj.module.train.controller.admin.vo.AdminUpdateCourseReqVO;
import com.hsjk.szwj.module.train.controller.app.vo.baijiayun.PlayerTokenRespVO;
import com.hsjk.szwj.module.train.controller.app.vo.course.AppCourseDetailRespVO;
import com.hsjk.szwj.module.train.controller.app.vo.course.AppCourseItemQuestionSetRespVO;
import com.hsjk.szwj.module.train.controller.app.vo.course.AppCourseItemRespVO;
import com.hsjk.szwj.module.train.controller.app.vo.course.AppCourseLiveInfoRespVO;
import com.hsjk.szwj.module.train.controller.app.vo.course.AppCoursePageReqVO;
import com.hsjk.szwj.module.train.controller.app.vo.course.AppCoursePageRespVO;
import com.hsjk.szwj.module.train.controller.app.vo.course.AppCourseWithPriceRespVO;
import com.hsjk.szwj.module.train.convert.AppCourseConvert;
import com.hsjk.szwj.module.train.convert.CourseConvert;
import com.hsjk.szwj.module.train.convert.ProductConvert;
import com.hsjk.szwj.module.train.dal.dataobject.config.CourseMockExamConfigDO;
import com.hsjk.szwj.module.train.dal.dataobject.course.CourseArticleDO;
import com.hsjk.szwj.module.train.dal.dataobject.course.CourseCatalogDO;
import com.hsjk.szwj.module.train.dal.dataobject.course.CourseDO;
import com.hsjk.szwj.module.train.dal.dataobject.course.CourseItemDO;
import com.hsjk.szwj.module.train.dal.dataobject.course.CourseLiveDO;
import com.hsjk.szwj.module.train.dal.dataobject.course.CourseStudyRecordDO;
import com.hsjk.szwj.module.train.dal.dataobject.course.CourseTagDO;
import com.hsjk.szwj.module.train.dal.dataobject.course.CourseVideoDO;
import com.hsjk.szwj.module.train.dal.dataobject.course.CourseVideoPointDO;
import com.hsjk.szwj.module.train.dal.dataobject.course.PlatformCourseMappingDO;
import com.hsjk.szwj.module.train.dal.dataobject.product.ProductSkuBindDO;
import com.hsjk.szwj.module.train.dal.dataobject.product.ProductSkuDO;
import com.hsjk.szwj.module.train.dal.dataobject.product.ProductSpuDO;
import com.hsjk.szwj.module.train.dal.dataobject.question.QuestionSetDO;
import com.hsjk.szwj.module.train.dal.dataobject.question.QuestionSetMappingDO;
import com.hsjk.szwj.module.train.dal.mysql.config.ICourseMockExamConfigMapper;
import com.hsjk.szwj.module.train.dal.mysql.course.ICourseArticleMapper;
import com.hsjk.szwj.module.train.dal.mysql.course.ICourseCatalogMapper;
import com.hsjk.szwj.module.train.dal.mysql.course.ICourseItemMapper;
import com.hsjk.szwj.module.train.dal.mysql.course.ICourseLiveMapper;
import com.hsjk.szwj.module.train.dal.mysql.course.ICourseMapper;
import com.hsjk.szwj.module.train.dal.mysql.course.ICourseStudyRecordMapper;
import com.hsjk.szwj.module.train.dal.mysql.course.ICourseTagMapper;
import com.hsjk.szwj.module.train.dal.mysql.course.ICourseTagMappingMapper;
import com.hsjk.szwj.module.train.dal.mysql.course.ICourseVideoMapper;
import com.hsjk.szwj.module.train.dal.mysql.course.ICourseVideoPointMapper;
import com.hsjk.szwj.module.train.dal.mysql.course.IPlatformCourseMappingMapper;
import com.hsjk.szwj.module.train.dal.mysql.product.IProductSkuBindMapper;
import com.hsjk.szwj.module.train.dal.mysql.product.IProductSkuMapper;
import com.hsjk.szwj.module.train.dal.mysql.product.IProductSpuMapper;
import com.hsjk.szwj.module.train.dal.mysql.question.IQuestionSetMapper;
import com.hsjk.szwj.module.train.dal.mysql.question.IQuestionSetMappingMapper;
import com.hsjk.szwj.module.train.enums.CourseItemTypeEnum;
import com.hsjk.szwj.module.train.enums.ProductSkuBindTypeEnum;
import com.hsjk.szwj.module.train.service.ICourseItemService;
import com.hsjk.szwj.module.train.service.ICourseService;
import com.hsjk.szwj.module.train.service.ISkuDivisionService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.hsjk.szwj.framework.web.core.util.WebFrameworkUtils.getLoginUserId;

/**
 * @author chenjiawei
 * @desc
 * @Date 2023/08/04 01:13
 **/
@Service
@Slf4j
public class CourseServiceImpl implements ICourseService {

    @Resource
    private ICourseTagMapper courseTagMapper;

    @Resource
    private ICourseCatalogMapper courseCatalogMapper;

    @Resource
    private ICourseItemMapper courseItemMapper;

    @Resource
    private ICourseVideoMapper courseVideoMapper;

    @Resource
    private ICourseArticleMapper courseArticleMapper;

    @Resource
    private IProductSpuMapper productSpuMapper;

    @Resource
    private IProductSkuMapper productSkuMapper;

    @Resource
    private IProductSkuBindMapper productSkuBindMapper;

    @Resource
    private ICourseMapper courseMapper;

    @Resource
    private ICourseLiveMapper courseLiveMapper;


    @Resource
    private MemberUserApi memberUserApi;

    @Resource
    private ICourseStudyRecordMapper courseStudyRecordMapper;

    @Resource
    private ICourseVideoPointMapper courseVideoPointMapper;

    @Resource
    private ICourseTagMappingMapper courseTagMappingMapper;

    @Resource
    private IQuestionSetMapper questionSetMapper;

    @Resource
    private IQuestionSetMappingMapper questionSetMappingMapper;

    @Resource
    private ICourseMockExamConfigMapper courseMockExamConfigMapper;

    @Resource
    private IPlatformCourseMappingMapper platformCourseMappingMapper;

    @Resource
    private ICourseItemService courseItemService;

    @Resource
    private TenantApi tenantApi;

    @Resource
    private ISkuDivisionService skuDivisionService;

    @Resource
    private IMchDivisionReceiverApi divisionReceiverApi;
    @Resource
    private RedisTemplate redisTemplate;


    @Value("${szwj.platform-tenant-id}")
    private String platformTenantId;


    @Override
    public List<CourseTagDO> getCourseTagList() {
        return courseTagMapper.getCourseTagList();
    }

    @Override
    @TenantIgnore
    public PageResult<AppCourseWithPriceRespVO> getCoursePage(AppCoursePageReqVO reqVO) {
        Page<AppCoursePageReqVO> page = new Page<>(reqVO.getPageNo(), reqVO.getPageSize());
        Long tenantId = TenantContextHolder.getTenantId();
//        Long tenantId = 1699754050142015488L;
        String carType = reqVO.getCarType();
        IPage<AppCourseWithPriceRespVO> pageResult = productSpuMapper.selectSpuCoursePage(page, tenantId,carType);
        List<AppCourseWithPriceRespVO> records = pageResult.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            for (AppCourseWithPriceRespVO respVO : records) {
                List<String> tags = courseTagMappingMapper.getTagsByCourseId(respVO.getId());
                respVO.setTags(tags);
            }
        }
        return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
    }

    @Override
    @TenantIgnore
    public PageResult<AppCoursePageRespVO> getPurchasedCoursePage(AppCoursePageReqVO reqVO) {
        List<Long> tenantIds = new ArrayList<>();
        Long tenantId = TenantContextHolder.getTenantId();
        if (ObjectUtil.isNotNull(tenantId)) {
            tenantIds.add(tenantId);
        }
        if (ObjectUtil.isNotNull(platformTenantId)) {
            tenantIds.add(Long.valueOf(platformTenantId));
        }
        IPage<AppCoursePageRespVO> appCoursePageRespVOIPage = courseMapper.selectPurchasedCoursePage(new Page<>(reqVO.getPageNo(), reqVO.getPageSize())
                , getLoginUserId(), tenantIds);
        if (CollUtil.isNotEmpty(appCoursePageRespVOIPage.getRecords())) {
            for (AppCoursePageRespVO respVO : appCoursePageRespVOIPage.getRecords()) {
                List<String> tags = courseTagMappingMapper.getTagsByCourseId(respVO.getId());
                respVO.setTags(tags);
            }
        }
        return new PageResult<>(appCoursePageRespVOIPage.getRecords(), appCoursePageRespVOIPage.getTotal());
    }

    @Override
    @TenantIgnore
    public AppCourseDetailRespVO getCourseDetailById(Long id) {
        CourseDO courseDO = courseMapper.selectById(id);
        ProductSkuDO productSkuDO = productSkuBindMapper.selectSkuByCourseId(id);
        Boolean purchased = courseMapper.selectIsPurchased(getLoginUserId(), id) > 0;

        AppCourseDetailRespVO appCourseDetailRespVO = AppCourseConvert.INSTANCE.convertAppCourseDetailRespVO(courseDO);
        if (productSkuDO != null) {
            appCourseDetailRespVO.setPrice(productSkuDO.getPrice());
            appCourseDetailRespVO.setSkuId(productSkuDO.getId());
            appCourseDetailRespVO.setSkuPic(productSkuDO.getPicUrl());
            appCourseDetailRespVO.setPayNote(productSkuDO.getPayNote());
        }

        appCourseDetailRespVO.setPurchased(purchased);
        if (ObjectUtil.isNotNull(courseDO)) {
            List<String> tags = courseTagMappingMapper.getTagsByCourseId(courseDO.getId());
            appCourseDetailRespVO.setTags(tags);
        }
        CourseMockExamConfigDO courseMockExamConfigDO = courseMockExamConfigMapper.selectOne(new LambdaQueryWrapperX<CourseMockExamConfigDO>().eq(CourseMockExamConfigDO::getCourseId, id));
        if (ObjectUtil.isNotNull(courseMockExamConfigDO)) {
            appCourseDetailRespVO.setIfContainMockExam(Boolean.TRUE);
            appCourseDetailRespVO.setSubject(courseMockExamConfigDO.getSubject());
            appCourseDetailRespVO.setCarType(courseMockExamConfigDO.getCarType());
        }
        return appCourseDetailRespVO;
    }

    @Override
    @TenantIgnore
    public List<CourseCatalogDO> getCourseCatalogListByCourseId(Long courseId) {
        if (ObjectUtil.isEmpty(courseId)) {
            throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "课程ID不能为空");
        }
        return courseCatalogMapper.getCourseCatalogListByCourseId(courseId);
    }

    @Override
    @TenantIgnore
    public List<AppCourseItemRespVO> getCourseItemListByCourseId(Long courseId) {
        if (ObjectUtil.isEmpty(courseId)) {
            throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "课程ID不能为空");
        }

        List<CourseItemDO> courseItemDOS = courseItemMapper.selectList(new LambdaQueryWrapperX<CourseItemDO>().eq(CourseItemDO::getCourseId, courseId).orderByAsc(CourseItemDO::getSortNo));

        // 查询学习记录
        Map<Long, CourseStudyRecordDO> studyRecordMap;
        if (!courseItemDOS.isEmpty()) {
            List<CourseStudyRecordDO> courseStudyRecordDOS = courseStudyRecordMapper.selectList(new LambdaQueryWrapperX<CourseStudyRecordDO>().eq(CourseStudyRecordDO::getUserId, getLoginUserId()).in(CourseStudyRecordDO::getItemId, courseItemDOS.stream().map(CourseItemDO::getId).toArray()));
            studyRecordMap = courseStudyRecordDOS.stream().collect(HashMap::new, (map, item) -> map.put(item.getItemId(), item), HashMap::putAll);
        } else {
            studyRecordMap = new HashMap<>();
        }

        return courseItemDOS.stream().map(item -> {
            AppCourseItemRespVO respVO = AppCourseConvert.INSTANCE.convert(item);
            CourseStudyRecordDO studyRecordDO = studyRecordMap.get(item.getId());
            if (studyRecordDO != null) {
                respVO.setStudyRecord(AppCourseConvert.INSTANCE.convert(studyRecordDO));
            }
            return respVO;
        }).collect(Collectors.toList());
    }

    @Override
    public CourseVideoDO getCourseVideoById(Long id) {
        if (ObjectUtil.isEmpty(id)) {
            throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "ID不能为空");
        }
        return courseVideoMapper.selectById(id);
    }

    @Override
    public CourseArticleDO getArticleById(Long id) {
        if (ObjectUtil.isEmpty(id)) {
            throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "ID不能为空");
        }
        return courseArticleMapper.selectById(id);
    }

    private String getLiveSign(Map<String, Object> params, TenantRespDTO tenantInfo) {
        // 将params中的key按照字典顺序升序排序
        String str = params.keySet().stream().sorted().map(key -> {
            Object value = params.get(key);
            if (value instanceof String) {
                return key + "=" + value;
            } else {
                return key + "=" + value.toString();
            }
        }).reduce((a, b) -> a + "&" + b).orElse("");
        str += "&partner_key=" + tenantInfo.getBjyPartnerKey();

        // 计算32位的小写的md5值
        return DigestUtils.md5DigestAsHex(str.getBytes()).toLowerCase();
    }

    private TenantRespDTO getTenantInfo(Long tenantId) {
        TenantRespDTO tenantInfo = (TenantRespDTO) redisTemplate.opsForValue()
                .get(RedisKeyConstant.TENANT_INFO_KEY + tenantId);
        if (ObjectUtil.isNull(tenantInfo)) {
            tenantInfo = tenantApi.getTenantInfo(tenantId);
            redisTemplate.opsForValue().set(RedisKeyConstant.TENANT_INFO_KEY + tenantId, tenantInfo);
        }
        return tenantInfo;
    }

    private PlayerTokenRespVO getPlaybackPlayerToken(String roomId, Long tenantId) {
        TenantRespDTO tenantInfo = getTenantInfo(tenantId);
        String url = "https://" + tenantInfo.getBjyDomain() + ".at.baijiayun.com/openapi/playback/getPlayerToken";
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("partner_id", tenantInfo.getBjyPartnerId());
        paramMap.put("room_id", roomId);
        paramMap.put("expires_in", 10);
        paramMap.put("timestamp", System.currentTimeMillis());
        String sign = getLiveSign(paramMap, tenantInfo);
        paramMap.put("sign", sign);
        String result = HttpUtil.post(url, paramMap);
        log.info("getPlaybackPlayerToken:result:" + result);
        JSONObject resultObj = JSONUtil.parseObj(result).getJSONObject("data");

        if (resultObj == null) {
            return null;
        }

        PlayerTokenRespVO playerTokenRespVO = new PlayerTokenRespVO();
        playerTokenRespVO.setVideoId(resultObj.getLong("video_id"));
        playerTokenRespVO.setToken(resultObj.getStr("token"));

        return playerTokenRespVO;
    }

    private Integer getLiveStatus(CourseLiveDO liveDO) {
        TenantRespDTO tenantInfo = getTenantInfo(liveDO.getTenantId());
        String url = "https://" + tenantInfo.getBjyDomain() + ".at.baijiayun.com/openapi/live/getLiveStatus";
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("room_id", liveDO.getRoomId());
        paramMap.put("partner_id", tenantInfo.getBjyPartnerId());
        paramMap.put("timestamp", System.currentTimeMillis());
        String sign = getLiveSign(paramMap, tenantInfo);
        paramMap.put("sign", sign);

        String body = HttpUtil.get(url, paramMap);
        JSONObject bodyObject = JSONUtil.parseObj(body);
        // 直播间状态：1 上课中 0 不在上课中
        return bodyObject.getJSONObject("data").getInt("status");
    }

    @SneakyThrows
    private String getLiveUrl(CourseLiveDO liveDO, MemberUserRespDTO user) {
        TenantRespDTO tenantInfo = getTenantInfo(liveDO.getTenantId());
        // 获取直播url
        String liveUrl = "https://" + tenantInfo.getBjyDomain() + ".at.baijiayun.com/web/room/enter?";
        HashMap<String, Object> urlParamMap = new HashMap<>();
        urlParamMap.put("room_id", liveDO.getRoomId());
        urlParamMap.put("user_number", user.getId());
        urlParamMap.put("user_role", 0);
        urlParamMap.put("user_name", user.getNickname());
        urlParamMap.put("user_avatar", URLEncodeUtil.encode(user.getAvatar(), StandardCharsets.UTF_8));
        String urlSign = getLiveSign(urlParamMap, tenantInfo);
        urlParamMap.put("sign", urlSign);
        liveUrl += urlParamMap.keySet().stream().map(key -> key + "=" + urlParamMap.get(key)).reduce((a, b) -> a + "&" + b).get();
        return liveUrl;
    }

    @SneakyThrows
    private String getPlaybackUrl(CourseLiveDO liveDO, MemberUserRespDTO user) {
        // 获取回放url
        PlayerTokenRespVO playerTokenRespVO = getPlaybackPlayerToken(liveDO.getRoomId(), liveDO.getTenantId());

        if (playerTokenRespVO == null) {
            return null;
        }
        TenantRespDTO tenantInfo = getTenantInfo(liveDO.getTenantId());
        HashMap<String, Object> urlParamMap = new HashMap<>();
        urlParamMap.put("classid", liveDO.getRoomId());
        urlParamMap.put("token", playerTokenRespVO.getToken());
        urlParamMap.put("user_number", user.getId());
        urlParamMap.put("user_name", URLEncodeUtil.encode(user.getNickname(), StandardCharsets.UTF_8));
        String playbackUrl = "https://" + tenantInfo.getDomain() + ".at.baijiayun.com/web/playback/index?";
        playbackUrl += urlParamMap.keySet().stream().map(key -> key + "=" + urlParamMap.get(key)).reduce((a, b) -> a + "&" + b).get();
        return playbackUrl;
    }

    @Override
    public AppCourseLiveInfoRespVO getLiveInfoByLiveId(Long liveId) {
        if (ObjectUtil.isEmpty(liveId)) {
            throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "直播ID不能为空");
        }

        CourseLiveDO liveDO = courseLiveMapper.selectById(liveId);
        if (ObjectUtil.isEmpty(liveDO)) {
            throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "直播不存在");
        }

        // 获取对应用户
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        MemberUserRespDTO user = memberUserApi.getUser(loginUserId);

        // 获取是否购买
        Integer buyRecord = courseItemService.getMemberLiveCourse(liveId, loginUserId);
        if (buyRecord == 0) {
            throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "未购买该直播");
        }

        // 获取直播状态
        Integer status = getLiveStatus(liveDO);

        AppCourseLiveInfoRespVO liveInfoRespVO = new AppCourseLiveInfoRespVO();
        liveInfoRespVO.setLiveStatus(status);

        if (status == 1) {
            String liveUrl = getLiveUrl(liveDO, user);
            // 返回直播链接
            liveInfoRespVO.setLiveUrl(liveUrl);
        } else {
            String playbackUrl = getPlaybackUrl(liveDO, user);
            if (playbackUrl == null) {
                // 直播未开始，返回空
                liveInfoRespVO.setLiveStatus(-1);
                liveInfoRespVO.setLiveUrl("");
                return liveInfoRespVO;
            }
            // 返回回放链接
            liveInfoRespVO.setLiveUrl(playbackUrl);
        }
        return liveInfoRespVO;
    }

    @Override
    public boolean updateStudyRecord(CourseStudyRecordDO studyRecordDO) {
        if (ObjectUtil.isEmpty(studyRecordDO)) {
            throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "学习记录不能为空");
        }
        CourseStudyRecordDO courseStudyRecordDO = courseStudyRecordMapper.selectOne(new LambdaQueryWrapperX<CourseStudyRecordDO>().eq(CourseStudyRecordDO::getCourseId, studyRecordDO.getCourseId()).eq(CourseStudyRecordDO::getItemId, studyRecordDO.getItemId()).eq(CourseStudyRecordDO::getUserId, getLoginUserId()));

        if (ObjectUtil.isEmpty(courseStudyRecordDO)) {
            studyRecordDO.setUserId(getLoginUserId());
            int insert = courseStudyRecordMapper.insert(studyRecordDO);
            return insert > 0;
        }

        studyRecordDO.setId(courseStudyRecordDO.getId());
        int updatedCount = courseStudyRecordMapper.updateById(studyRecordDO);
        return updatedCount > 0;
    }

    @Override
    public List<CourseVideoPointDO> getVideoPointByVideoId(Long videoId) {
        return courseVideoPointMapper.selectList(new LambdaQueryWrapperX<CourseVideoPointDO>().eq(CourseVideoPointDO::getVideoId, videoId));
    }

    @Override
    @TenantIgnore
    public String getPlayerTokenByVideoId(Long videoId) {
        CourseVideoDO courseVideoDO = courseVideoMapper.selectById(videoId);
        if (ObjectUtil.isNull(courseVideoDO)) {
            return null;
        }
        TenantRespDTO tenantInfo = getTenantInfo(courseVideoDO.getTenantId());
        String url = "https://" + tenantInfo.getBjyDomain() + ".at.baijiayun.com/openapi/video/getPlayerToken";
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("video_id", courseVideoDO.getBjyVideoId());
        paramMap.put("partner_id", tenantInfo.getBjyPartnerId());
        //10分钟过期
        paramMap.put("expires_in", 10 * 60);
        paramMap.put("timestamp", System.currentTimeMillis());
        String sign = getLiveSign(paramMap, tenantInfo);
        paramMap.put("sign", sign);

        String body = HttpUtil.post(url, paramMap);
        log.info(body);
        JSONObject bodyObject = JSONUtil.parseObj(body);
        if (ObjectUtil.isNotNull(bodyObject)) {
            Integer code = bodyObject.getInt("code");
            if (code != 0) {
                log.error("getPlayerTokenByVideoId,{}", bodyObject.getStr("msg"));
                return null;
            }
            // 直播间状态：1 上课中 0 不在上课中
            return bodyObject.getJSONObject("data").getStr("token");
        } else {
            return null;
        }
    }

    @Override
    @TenantIgnore
    public List<Object> getCatalogItemList(Long courseCatalogId) {
        if (ObjectUtil.isNull(courseCatalogId)) {
            throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "参数不能为空");
        }
        List<Object> typeObjList = new ArrayList<>();
        List<CourseItemDO> listByCatalogId = courseItemMapper.getListByCatalogId(courseCatalogId);
        for (CourseItemDO courseItem : listByCatalogId) {
            if (CourseItemTypeEnum.VIDEO.getValue().equals(courseItem.getCourseItemType())) {
                CourseVideoDO courseVideoDO = courseVideoMapper.selectById(courseItem.getCourseItemTypeId());
                typeObjList.add(AppCourseConvert.INSTANCE.convertAppCourseVideoListRespVO(courseVideoDO));
            } else if (CourseItemTypeEnum.IMAGE_TEXT.getValue().equals(courseItem.getCourseItemType())) {
                CourseArticleDO courseArticleDO = courseArticleMapper.selectById(courseItem.getCourseItemTypeId());
                typeObjList.add(AppCourseConvert.INSTANCE.convertAppCourseArticleListRespVO(courseArticleDO));
            } else if (CourseItemTypeEnum.SUBJECT.getValue().equals(courseItem.getCourseItemType())) {
                AppCourseItemQuestionSetRespVO courseItemQuestionSetInfo = this.getCourseItemQuestionSetInfo(courseItem.getCourseItemTypeId(), courseItem.getId());
                typeObjList.add(courseItemQuestionSetInfo);
            } else if (CourseItemTypeEnum.LIVE.getValue().equals(courseItem.getCourseItemType())) {
                CourseLiveDO courseLiveDO = courseLiveMapper.selectById(courseItem.getCourseItemTypeId());
                typeObjList.add(AppCourseConvert.INSTANCE.convert(courseLiveDO));
            }
        }
        return typeObjList;
    }

    @Override
    public PageResult<AdminCoursePageRespVO> getAdminCoursePage(AdminCoursePageReqVO reqVO) {
        Page<AdminCoursePageReqVO> page = new Page<>(reqVO.getPageNo(), reqVO.getPageSize());
        IPage<AdminCoursePageRespVO> adminCoursePageRespVOIPage = courseMapper.selectAdminPage(page, reqVO);
        return new PageResult<>(adminCoursePageRespVOIPage.getRecords(), adminCoursePageRespVOIPage.getTotal());
    }

    @Override
    @TenantIgnore
    public PageResult<AdminPlatformCoursePageRespVO> getAdminPlatformCoursePage(AdminPlatformCoursePageReqVO reqVO) {
        Page<AdminPlatformCoursePageReqVO> page = new Page<>(reqVO.getPageNo(), reqVO.getPageSize());
        IPage<AdminPlatformCoursePageRespVO> adminPlatformCoursePage = courseMapper.getAdminPlatformCoursePage(page, reqVO,
                Long.valueOf(platformTenantId));
        List<AdminPlatformCoursePageRespVO> records = adminPlatformCoursePage.getRecords();
        Long tenantId = TenantContextHolder.getTenantId();
        if (CollUtil.isNotEmpty(records)) {
            for (AdminPlatformCoursePageRespVO vo : records) {
                PlatformCourseMappingDO mappingDO = platformCourseMappingMapper.selectOne(new LambdaQueryWrapperX<PlatformCourseMappingDO>()
                        .eq(PlatformCourseMappingDO::getPlatformCourseId, vo.getId())
                        .eq(PlatformCourseMappingDO::getTenantId, tenantId));
                if (ObjectUtil.isNotNull(mappingDO)) {
                    vo.setIfBind(Boolean.TRUE);
                } else {
                    vo.setIfBind(Boolean.FALSE);
                }
            }
        }
        return new PageResult<>(adminPlatformCoursePage.getRecords(), adminPlatformCoursePage.getTotal());
    }

    @Override
    @TenantIgnore
    public AdminCourseDetailRespVO getAdminCourseDetail(Long id) {
        return courseMapper.selectAdminDetail(id);
    }

    @Override
    public PageResult<AdminMemberCoursePageRespVO> memberCoursePage(AdminMemberCoursePageReqVO reqVO) {
        Page<AdminMemberCoursePageReqVO> page = new Page<>(reqVO.getPageNo(), reqVO.getPageSize());
        IPage<AdminMemberCoursePageRespVO> adminMemberCoursePageRespVOIPage = courseMapper.memberCoursePage(page, reqVO);
        return new PageResult<>(adminMemberCoursePageRespVOIPage.getRecords(), adminMemberCoursePageRespVOIPage.getTotal());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean createCourse(AdminCreateCourseReqVO reqVO) {
        boolean ck = false;
        Long tenantId = TenantContextHolder.getTenantId();
        CourseDO courseDO = CourseConvert.INSTANCE.convertCourseDO(reqVO);
        // 启用状态
        courseDO.setCourseStatus(CommonConstant.PUB_USABLE);
        int insertResult = courseMapper.insert(courseDO);
        if (insertResult > 0) {
            ProductSpuDO productSpuDO = ProductConvert.INSTANCE.convertProductSpuDO(reqVO.getSpuInfo());
            // 课程商品
            productSpuDO.setSpuType(2);
            productSpuDO.setDetail(" ");
            productSpuDO.setPicUrls("{}");
            int spuInsertResult = productSpuMapper.insert(productSpuDO);
            if (spuInsertResult > 0) {
                ProductSkuDO productSkuDO = ProductConvert.INSTANCE.convertProductSkuDO(reqVO.getSkuInfo());
                productSkuDO.setSpuId(productSpuDO.getId());
                productSkuDO.setSkuName(reqVO.getCourseName());
                productSkuDO.setPicUrl(reqVO.getCourseCoverUrl());
                int skuInsertResult = productSkuMapper.insert(productSkuDO);
                if (skuInsertResult > 0) {
                    //如果是平台课程创建，默认设置分账为30%
                    if (platformTenantId.equals(String.valueOf(tenantId))) {
                        MchDivisionReceiverDTO mchDivisionReceiver = divisionReceiverApi.getMchDivisionReceiverByTenantId(tenantId);
                        AdminSetSkuDivisionReqVO divisionReqVO = new AdminSetSkuDivisionReqVO();
                        divisionReqVO.setDivisionProfit(0.3);
                        divisionReqVO.setDivisionModel(CommonConstant.DIVISION_MODEL_PERCENTAGE);
                        divisionReqVO.setDivisionAmount(0);
                        divisionReqVO.setSkuId(productSkuDO.getId());
                        divisionReqVO.setMchDivisionReceiverId(mchDivisionReceiver.getId());
                        skuDivisionService.createSkuDivision(divisionReqVO);
                    }
                    ProductSkuBindDO productSkuBindDO = new ProductSkuBindDO();
                    productSkuBindDO.setSkuId(productSkuDO.getId());
                    // 课程类型
                    productSkuBindDO.setBindType(ProductSkuBindTypeEnum.COURSE.getValue());
                    productSkuBindDO.setBindId(courseDO.getId());
                    int insert = productSkuBindMapper.insert(productSkuBindDO);
                    if (insert > 0) {
                        ck = true;
                    }
                }
            }
        }
        return ck;
    }

    @Override
    public Boolean updateCourse(AdminUpdateCourseReqVO reqVO) {
        boolean ck = false;
        CourseDO courseDO = CourseConvert.INSTANCE.convertCourseDO(reqVO);
        courseDO.setCarType(reqVO.getCarType());
        int updateResult = courseMapper.updateById(courseDO);
        ProductSpuDO productSpuDO = ProductConvert.INSTANCE.convertProductSpuDO(reqVO.getSpuInfo());
        int spuUpdateResult = productSpuMapper.updateById(productSpuDO);
        ProductSkuDO productSkuDO = ProductConvert.INSTANCE.convertProductSkuDO(reqVO.getSkuInfo());
        int skuUpdateResult = productSkuMapper.updateById(productSkuDO);
        ProductSkuBindDO productSkuBindDO = new ProductSkuBindDO();
        int update = productSkuBindMapper.updateById(productSkuBindDO);
        if (updateResult > 0 && spuUpdateResult > 0 && skuUpdateResult > 0 && update > 0) {
            ck = true;
        }
        return ck;
    }

    @Override
    public AppCourseItemQuestionSetRespVO getCourseItemQuestionSetInfo(Long questionSetId, Long courseItemId) {
        AppCourseItemQuestionSetRespVO respVO = new AppCourseItemQuestionSetRespVO();
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        Long courseItemQuestionSetActionCount = courseMapper.getCourseItemQuestionSetActionCount(questionSetId
                , courseItemId, loginUserId);
        QuestionSetDO questionSetDO = questionSetMapper.selectById(questionSetId);
        if (ObjectUtil.isNotNull(questionSetDO)) {
            respVO.setId(questionSetDO.getId());
            respVO.setSubject(questionSetDO.getSubject());
            respVO.setCarType(questionSetDO.getCarType());
            respVO.setActionCount(courseItemQuestionSetActionCount);
            Long count = questionSetMappingMapper.selectCount(new LambdaQueryWrapper<QuestionSetMappingDO>()
                    .eq(QuestionSetMappingDO::getQuestionSetId, questionSetDO.getId()));
            respVO.setTotalCount(count);
        }
        return respVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteCourse(Long id) {
        List<ProductSkuBindDO> bindList = productSkuBindMapper.getProductSkuBindListByCourseId(id);
        if (CollUtil.isNotEmpty(bindList)) {
            List<Long> skuDeleteIds = new ArrayList<>();
            for (ProductSkuBindDO bindDO : bindList) {
                skuDeleteIds.add(bindDO.getSkuId());
            }
            if (CollUtil.isNotEmpty(skuDeleteIds)) {
                List<Long> spuDeleteIds = new ArrayList<>();
                for (Long skuId : skuDeleteIds) {
                    ProductSkuDO productSkuDO = productSkuMapper.selectById(skuId);
                    if (ObjectUtil.isNotNull(productSkuDO)) {
                        spuDeleteIds.add(productSkuDO.getSpuId());
                    }
                }
                if (CollUtil.isNotEmpty(spuDeleteIds)) {
                    productSpuMapper.deleteBatchIds(spuDeleteIds);
                }
                productSkuMapper.deleteBatchIds(skuDeleteIds);
            }
        }
        return Boolean.TRUE;
    }
}
